﻿using System.Net;
using HelpDeskTool.Entities.Devices;
using Microsoft.Management.Infrastructure;

namespace HelpDeskTool.Services;

public class Cimv2
{
	private static readonly string rootCimv2 = @"root\cimv2";
	private static readonly string rootWmi = @"root\wmi";
	private static readonly string queryDialect = "WQL";
	private static readonly string[] interfaceTypes = new[]
	{
		"IDE", "SCSI"
	};

	protected static DateTime? GetLastBootUp(CimSession cimSession)
	{
		var dt = GetWmiInfo(cimSession, "Win32_OperatingSystem", "LastBootUpTime").FirstOrDefault();

		return dt != null
			? Convert.ToDateTime(dt)
			: null;
	}

	protected static string GetOperatingSystem(CimSession cimSession)
	{
		return GetWmiInfo(cimSession, "Win32_OperatingSystem", "Caption, OSArchitecture").ToArray().First();
	}

	protected  static Motherboard GetMotherBoard(CimSession cimSession)
	{
		var device = GetWmiInfo(cimSession, "Win32_BaseBoard", "Manufacturer, Product").First();

		return new Motherboard { ModelName = device };
	}

	protected static CPU[] GetCPU(CimSession cimSession)
	{
		var devices = GetWmiInfo(cimSession, "Win32_Processor", "Name");

		return devices
			.Select(x => new CPU { ModelName = x })
			.ToArray();
	}

	protected static GPU[] GetGPU(CimSession cimSession)
	{
		var devices = GetWmiInfo(cimSession, "Win32_VideoController", "Name");

		return devices
			.Select(x => new GPU { ModelName = x })
			.ToArray();
	}

	protected static NetworkAdapter[] GetNetworkAdapters(CimSession cimSession)
	{
		var devices = GetWmiInfo(cimSession, "Win32_NetworkAdapterConfiguration WHERE IPEnabled = 'TRUE'", "Description, MACAddress, IPAddress", ";");

		return devices
			.Select(x =>
			{
				var info = x.Split(';', StringSplitOptions.TrimEntries);

				return new NetworkAdapter
				{
					ModelName = info[0],
					IpAddress = IPAddress.Parse(info[1]),
					MacAddress = info[2]
				};
			})
			.ToArray();
	}

	protected static IEnumerable<string> GetWmiInfo(CimSession cimSession, string provider, string keys, string separator = " ")
	{
		var collection = cimSession.QueryInstances(rootCimv2, queryDialect, $"SELECT {keys} FROM {provider}");

		Func<CimProperty, bool> filter = x => x.Value != null && (keys == "*" || keys.Contains(x.Name, StringComparison.OrdinalIgnoreCase));

		foreach (var item in collection)
		{
			var props = item.CimInstanceProperties
				.Where(filter)
				.OrderBy(x => x.Name)
				.ToArray();

			var selected = props
				.Select(p => p.Value.GetType().IsArray ? ((string[])p.Value)?
				.FirstOrDefault() : p.Value.ToString()?.Trim());

			yield return string.Join(separator, selected);
		}
	}

	protected static IEnumerable<Entities.Devices.Monitor> GetInfoMonitors(CimSession cimSession)
	{
		var collection = cimSession.QueryInstances(rootWmi, queryDialect, $"SELECT UserFriendlyName FROM WmiMonitorId");

		foreach (var item in collection.ToArray())
		{
			var prop = item.CimInstanceProperties["UserFriendlyName"]?.Value as ushort[];

			if (prop == null || prop.Length < 1)
				yield break;

			var bytes = new byte[prop.Length * sizeof(ushort)];
			Buffer.BlockCopy(prop, 0, bytes, 0, bytes.Length);

			var result = System.Text.Encoding.UTF8.GetString(bytes)
				.Replace("\0", string.Empty);

			yield return new Entities.Devices.Monitor { ModelName = result };
		}
	}

	protected static IEnumerable<RamModule> GetInfoRamModules(CimSession cimSession)
	{
		var collection = cimSession.QueryInstances(rootCimv2, queryDialect, $"SELECT Manufacturer, Capacity, Speed FROM Win32_PhysicalMemory");
		foreach (var item in collection)
		{
			var props = item.CimInstanceProperties;
			var capacity = Convert.ToInt64(props["Capacity"].Value);

			yield return new RamModule
			{
				ModelName = props["Manufacturer"].Value.ToString(),
				Capacity = (uint)(capacity / 1024 / 1024),
				Speed = Convert.ToUInt32(props["Speed"].Value)
			};
		}
	}

	protected static IEnumerable<StorageDisk> GetInfoStorageDisks(CimSession cimSession)
	{
		var collection = cimSession.QueryInstances(rootCimv2, queryDialect, $"SELECT Model, Size, InterfaceType FROM Win32_DiskDrive");

		foreach (var item in collection)
		{
			var props = item.CimInstanceProperties;

			var interfaceType = props["InterfaceType"].Value.ToString();
			if (string.IsNullOrWhiteSpace(interfaceType))
				yield break;

			if (interfaceTypes.Contains(interfaceType))
			{
				var capacity = Convert.ToInt64(props["Size"].Value);

				yield return new StorageDisk
				{
					ModelName = props["Model"].Value.ToString(),
					Capacity = (uint)(capacity / 1000 / 1000 / 1000)
				};
			}
		}
	}
}
